home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / news / readers / nn / nn6.4.patch11 < prev    next >
Encoding:
Text File  |  1990-10-05  |  57.9 KB  |  2,292 lines

  1.          This is an official patch to nn release 6.4
  2.          -------------------------------------------
  3.  
  4.                    PATCH #11
  5.  
  6.                 Priority: HIGH
  7.  
  8.  
  9. This patch fixes a syntax error introduced in patch #10 which causes
  10. term.c not to compile on some systems.  It also fixes a nasty bug in
  11. the article header sorting which could provoke a crash in qsort;
  12. unfortunately the fix involves changing the interpretation of the
  13. subject-match-limit variable:
  14.  
  15. ===================================================================
  16.  
  17. The subject-match-limit variable is now a simple compare only first
  18. "length" characters limit whereas before it was only used if one
  19. subject was a true suffix of another subject.  Unfortunately, the old
  20. interpretation made the sorting non-deterministic and it could cause
  21. qsort() to crash.  The default value is now changed to 256 effectively
  22. disabling the match-limit unless explicitly set to a lower limit.
  23.  
  24. Thanks to Paul Eggert for a detailed analysis on this problem.
  25.  
  26. ===================================================================
  27.  
  28. This patch also changes the splitting of folders in a way which is not
  29. backwards compatible:  When splitting a folder (e.g. G +folder), nn
  30. will now determine the format of the folder (mail, mmdf, standard)
  31. from the *first* article in the folder, and the rest of the folder is
  32. split according to this format.  (Thanks to Bernd Wechner for a lot of
  33. patches to the folder and digest code).
  34.  
  35. This may cause problems with current folders which are a mixture of
  36. different formats; such folders must be manually reformatted to use
  37. only a single format.
  38.  
  39. To ensure that folders are used properly, the save-full and save-short
  40. commands will now append articles to an existing folder using the
  41. current format of the folder (determined from the first article).
  42. Consequently, the mmdf-format and mail-format variables are now only
  43. used to determine the type to use for new folders.
  44.  
  45. ===================================================================
  46.  
  47. Notice: Updates to the manual for the new features are postponed to
  48. the next patch to keep this patch at a reasonable size.
  49.  
  50. As usual, all changes are described in the updated RELEASE_NOTES file
  51. (read that for more details about this patch).  Thanks to all who
  52. reported bugs and provided fixes.
  53.  
  54. To apply this patch, use nn's :patch command, or run this command from
  55. the shell in the root of the nn source tree:
  56.     patch -p0 < this-article
  57.  
  58. Then run "make all" and "./inst u".
  59.  
  60. ++Kim Storm
  61.  
  62. ===================================================================
  63.  
  64. *** ./LAST/admin.c    Tue Sep 18 12:44:49 1990
  65. --- admin.c    Wed Sep 19 18:40:59 1990
  66. ***************
  67. *** 781,787 ****
  68.       char command[FILENAME*2];
  69.       char *rmprog;
  70.   
  71. !     if (!file_exist(news_active, "w")) {
  72.       printf("Not privileged to run rmgroup\n");
  73.       return;
  74.       }
  75. --- 781,787 ----
  76.       char command[FILENAME*2];
  77.       char *rmprog;
  78.   
  79. !     if (user_id != 0 && !file_exist(news_active, "w")) {
  80.       printf("Not privileged to run rmgroup\n");
  81.       return;
  82.       }
  83. *** ./LAST/aux.sh    Mon Jul  9 17:59:52 1990
  84. --- aux.sh    Tue Sep 25 16:08:24 1990
  85. ***************
  86. *** 334,343 ****
  87.       follow|post)
  88.         {
  89.       if ${POST_PIPE} ; then
  90. !       $POST < $FINAL
  91.         x=$?
  92.       else
  93. !       $POST $FINAL
  94.         x=$?
  95.       fi
  96.       case $x in
  97. --- 334,343 ----
  98.       follow|post)
  99.         {
  100.       if ${POST_PIPE} ; then
  101. !       $POST < $FINAL 2>&1
  102.         x=$?
  103.       else
  104. !       $POST $FINAL 2>&1
  105.         x=$?
  106.       fi
  107.       case $x in
  108. ***************
  109. *** 344,350 ****
  110.         0) sleep 60 ;;
  111.         *) echo $INEWS failed ;;
  112.       esac
  113. !       } | fgrep -v "mailing your article to" 
  114.         ;;
  115.   
  116.       *)
  117. --- 344,355 ----
  118.         0) sleep 60 ;;
  119.         *) echo $INEWS failed ;;
  120.       esac
  121. !       } | sed \
  122. !         -e "/spooled for later processing/d" \
  123. !         -e "/problem has been taken care of/d" \
  124. !         -e "/mailing your article to/d" \
  125. !         -e "/being mailed to/d" \
  126. !         -e "/is moderated/d"
  127.         ;;
  128.   
  129.       *)
  130. *** ./LAST/conf/m-symmetry.h    Sat Mar 31 23:12:50 1990
  131. --- conf/m-symmetry.h    Thu Aug 23 11:41:47 1990
  132. ***************
  133. *** 20,25 ****
  134. --- 20,28 ----
  135.   #define NO_VARARGS
  136.   #define STRCSPN
  137.   
  138. + /* enable parallel make hook in xmakefile */
  139. + #define PARALLEL_MAKE
  140.   /*
  141.    *    Not in network byte order on the 386
  142.    */
  143. *** ./LAST/conf/s-pyramid.h    Sat Mar 31 23:13:02 1990
  144. --- conf/s-pyramid.h    Mon Sep 24 13:14:00 1990
  145. ***************
  146. *** 9,13 ****
  147. --- 9,17 ----
  148.   
  149.   extern FILE *popen();
  150.   
  151. + /* The sysV shell has test built-in */
  152. + #undef SHELL
  153. + #define SHELL "/.attbin/sh"
  154.   #undef    MAILX
  155.   #define    MAILX    "/usr/.ucbucb/Mail"        /* BSD */
  156. *** ./LAST/db.c    Tue Sep 18 12:44:53 1990
  157. --- db.c    Thu Sep 20 18:01:05 1990
  158. ***************
  159. *** 22,27 ****
  160. --- 22,29 ----
  161.   
  162.   export int  reread_groups_file = 0;  /* nnmaster -G */
  163.   
  164. + export int  check_group_access = 0;
  165.   export data_header db_hdr;
  166.   export data_dynamic_data db_data;
  167.   
  168. ***************
  169. *** 97,102 ****
  170. --- 99,109 ----
  171.   
  172.       /* client */
  173.       if (gh->master_flag & M_NO_DIRECTORY) return 0;
  174. +     if (check_group_access && !use_nntp) {
  175. +     *p = NUL;
  176. +     if (file_exist(group_path_name, "dxr") == 0) return 0;
  177. +     }
  178.   
  179.       *p++ = '/';
  180.       *p = NUL;
  181. *** ./LAST/digest.c    Thu Jul 19 18:12:10 1990
  182. --- digest.c    Fri Oct  5 18:12:35 1990
  183. ***************
  184. *** 2,7 ****
  185. --- 2,11 ----
  186.    *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  187.    *
  188.    *    Digest article handling
  189. +  *
  190. +  *    The code to do the selective parsing of mail and mmdf formats,
  191. +  *    mail from lines and determining folder types is based on patches
  192. +  *    contributed by Bernd Wechner (bernd@bhpcpd.kembla.oz.au).
  193.    */
  194.   
  195.   #include "config.h"
  196. ***************
  197. *** 8,13 ****
  198. --- 12,19 ----
  199.   #include "news.h"
  200.   #include "debug.h"
  201.   
  202. + export int strict_from_parse = 2;
  203.   #ifdef DG_TEST
  204.   
  205.   #define TEST(fmt, x, y) if (Debug & DG_TEST) printf(fmt, x, y)
  206. ***************
  207. *** 49,59 ****
  208. --- 55,196 ----
  209.   
  210.   
  211.   /*
  212. +  * is_mail_from_line - Is this a legal unix mail "From " line?
  213. +  *
  214. +  * Given a line of input will check to see if it matches the standard
  215. +  * unix mail "from " header format. Returns 0 if it does and <0 if not.
  216. +  *
  217. +  * The check may be very lax or very strict depending upon
  218. +  * the value of "strict-mail-from-parse":
  219. +  *
  220. +  * 0 - Lax, checks only for the string "From ".
  221. +  * 1 - Strict, checks that the correct number of fields are present.
  222. +  * 2 - Very strict, also checks that each field contains a legal value.
  223. +  *
  224. +  * Assumptions: Not having the definitive unix mailbox reference I have
  225. +  * assumed that unix mailbox headers follow this format:
  226. +  *
  227. +  * From <person> <date> <garbage>
  228. +  *
  229. +  * Where <person> is the address of the sender, being an ordinary
  230. +  * string with no white space imbedded in it, and <date> is the date of
  231. +  * posting, in ctime(3C) format.
  232. +  *
  233. +  * This would, on the face of it, seem valid. I (Bernd) have yet to find a
  234. +  * unix mailbox header which doesn't follow this format.
  235. +  *
  236. +  * From: Bernd Wechner (bernd@bhpcpd.kembla.oz.au)
  237. +  * Obfuscated by: KFS (as usual)
  238. +  */
  239. + #define MAX_FIELDS 10
  240. + static char legal_day[]        = "SunMonTueWedThuFriSat";
  241. + static char legal_month[]    = "JanFebMarAprMayJunJulAugSepOctNovDec";
  242. + static int  legal_numbers[]    = { 1, 31, 0, 23, 0, 59, 0, 60, 1969, 2199 };
  243. + int is_mail_from_line(line, namebuf)
  244. + char *line;    /* Line of text to be checked */
  245. + char *namebuf;    /* Optional buffer to place packed sender info */
  246. + {
  247. +     char *fields[MAX_FIELDS];
  248. +     char *sender_tail;
  249. +     register char *lp, **fp;
  250. +     register int n, i;
  251. +     if (strncmp(line, "From ", 5)) return -100;
  252. +     if (strict_from_parse == 0) return 0;
  253. +     lp = line + 5;
  254. +     /* sender day mon dd hh:mm:ss year */
  255. +     for (n = 0, fp = fields; n < MAX_FIELDS; n++) {
  256. +     while (*lp && *lp != NL && isascii(*lp) && isspace(*lp)) lp++;
  257. +     if (*lp == NUL || *lp == NL) break;
  258. +     *fp++ = lp;
  259. +     while (*lp && isascii(*lp) && !isspace(*lp))
  260. +         if (*lp++ == ':' && (n == 4 || n == 5)) break;
  261. +     if (n == 0) sender_tail = lp;
  262. +     }
  263. +     if (n < 8) return -200-n;
  264. +     fp = fields;
  265. +     if (namebuf != NULL) {
  266. +     char x = *sender_tail;
  267. +     *sender_tail = NUL;
  268. +     pack_name(namebuf, *fp, NAME_LENGTH);
  269. +     *sender_tail = x;
  270. +     }
  271. +     if (strict_from_parse == 1) return 0;
  272. +     fp++;
  273. +     for (i = 0; i < 21; i += 3)
  274. +     if (strncmp(*fp, &legal_day[i], 3) == 0) break;
  275. +     if (i == 21) return -1;
  276. +     fp++;
  277. +     for (i = 0; i < 36; i += 3)
  278. +     if (strncmp(*fp, &legal_month[i], 3) == 0) break;
  279. +     if (i == 36) return -2;
  280. +     for (i = 0; i < 10; i += 2) {
  281. +     lp = *++fp;
  282. +     if (!isdigit(*lp)) return -20-i;
  283. +     n = atoi(lp);
  284. +     if (n < legal_numbers[i] || legal_numbers[i+1] < n) return -10-i;
  285. +     }
  286. +     return 0;
  287. + }
  288. + /*
  289.    * expect that f is positioned at header of an article
  290.    */
  291.   
  292.   static int is_mmdf_folder = 0;
  293. + static int is_mail_folder = 0;
  294.   
  295. + /*
  296. +  * get_folder_type
  297. +  *
  298. +  * Given a file descriptor f, will check what type of folder it is.
  299. +  * Must be called at zero offset and caller must reposition if necessary.
  300. +  * Side-effects: sets is_mail_folder, is_mmdf_folder, and current_folder_type.
  301. +  * Return values:
  302. +  *    -1: folder is empty,
  303. +  *     0: normal digest,
  304. +  *     1: UNIX mail format
  305. +  *     2: MMDF format
  306. +  */                                        
  307. + export int current_folder_type;
  308. + int get_folder_type(f)
  309. + FILE *f;
  310. + {
  311. +     char line[1024];
  312. +     int siz;
  313. +     is_mail_folder = 0;
  314. +     is_mmdf_folder = 0;
  315. +     if (fgets(line, 1024, f) == NULL)    
  316. +     return current_folder_type = -1;
  317. +     if (strncmp(line, "\001\001\001\001\n", 5) == 0) {
  318. +     is_mmdf_folder = 1;
  319. +     return current_folder_type = 2;
  320. +     }
  321. +     if (is_mail_from_line(line, (char *)NULL) == 0) {
  322. +     is_mail_folder = 1;
  323. +     return current_folder_type = 1;
  324. +     }
  325. +     return current_folder_type = 0;
  326. + }
  327.   get_digest_article(f, hdrbuf)
  328.   FILE *f;
  329.   news_header_buffer hdrbuf;
  330. ***************
  331. *** 183,198 ****
  332.   
  333.       TEST("\n>>%-.50s ==>>", line, 0);
  334.   
  335. !     if (line[0] == '\001' && strcmp(line, "\001\001\001\001\n") == 0) {
  336.       digest.dg_lpos = backup_p[backup_index];
  337. -     if (!is_mmdf_folder) fseek(f, digest.dg_lpos, 0);
  338.       --digest.dg_lines;
  339. -     is_mmdf_folder = 0;
  340.       return (digest.dg_lines <= 0) ? -1 : 1;
  341.       }
  342.   
  343. -     if (is_mmdf_folder) goto next_line;
  344.       for (cp = line; *cp && isascii(*cp) && isspace(*cp); cp++);
  345.   
  346.       if (*cp == NUL) {
  347. --- 320,335 ----
  348.   
  349.       TEST("\n>>%-.50s ==>>", line, 0);
  350.   
  351. !     if (is_mmdf_folder) {
  352. !     /* in an mmdf folder we simply look for the next ^A^A^A^A line */
  353. !     if (line[0] != '\001' || strcmp(line, "\001\001\001\001\n"))
  354. !         goto next_line;
  355.       digest.dg_lpos = backup_p[backup_index];
  356.       --digest.dg_lines;
  357.       return (digest.dg_lines <= 0) ? -1 : 1;
  358.       }
  359.   
  360.       for (cp = line; *cp && isascii(*cp) && isspace(*cp); cp++);
  361.   
  362.       if (*cp == NUL) {
  363. ***************
  364. *** 201,206 ****
  365. --- 338,353 ----
  366.       goto next_line;
  367.       }
  368.   
  369. +     if (is_mail_folder) {
  370. +     /* in a mail folder we simply look for the next "From " line */
  371. +     if (line[0] != 'F' || is_mail_from_line(line, (char *)NULL) < 0)
  372. +         goto next_line;
  373. +     line_type[backup_index] = LN_HEADER;
  374. +     fseek(f, backup_p[backup_index], 0);
  375. +     goto found_mail_header;
  376. +     }
  377.       blanks = cp - line;
  378.   
  379.       if (*cp == '-') {
  380. ***************
  381. *** 242,290 ****
  382.       goto next_line;
  383.       }
  384.   
  385. !     if (blanks == 0) {
  386. !     if (dg_hdr_field(line, 0)) {
  387. !         TEST("HEADER", 0, 0);
  388.   
  389. !         line_type[backup_index] = LN_HEADER;
  390. !         if (++more_header_lines < MIN_HEADER_LINES)
  391. !         goto next_possible_header_line;
  392. !         /* found block with MIN_HEADER_LINES */
  393. !         /* search for beginning of header */
  394.   
  395. !         TEST("\nSearch for start of header\n", 0, 0);
  396.   
  397. !         for (;;) {
  398. !         fseek(f, backup_p[backup_index], 0);
  399. !         --digest.dg_lines;
  400. !         if (--backup_count == 0) break;
  401. !         decrease_index();
  402. !         if ((line_type[backup_index] & (LN_HEADER | LN_TEXT)) == 0)
  403. !             break;
  404. !         }
  405.   
  406. !         if (digest.dg_lines == 0) {
  407. !         TEST("Skipped empty article\n", 0, 0);
  408. !         return -1;
  409. !         }
  410.   
  411. !         for (;;) {
  412. !         digest.dg_lpos = backup_p[backup_index];
  413. !         if (--backup_count < 0) break;
  414. !         decrease_index();
  415. !         if ((line_type[backup_index] & (LN_BLANK | LN_DASHED)) == 0)
  416. !             break;
  417. !         --digest.dg_lines;
  418. !         }
  419.   
  420. !         return (digest.dg_lines == 0) ? -1 : 1;
  421. !     }
  422. !     goto next_possible_header_line;
  423.       }
  424.   
  425. !     goto next_line;
  426.   }
  427.   
  428.   
  429. --- 389,436 ----
  430.       goto next_line;
  431.       }
  432.   
  433. !     if (blanks)
  434. !     goto next_line;
  435.   
  436. !     if (!dg_hdr_field(line, 0))
  437. !     goto next_possible_header_line;
  438.   
  439. !     TEST("HEADER", 0, 0);
  440.   
  441. !     line_type[backup_index] = LN_HEADER;
  442. !     if (++more_header_lines < MIN_HEADER_LINES)
  443. !     goto next_possible_header_line;
  444.   
  445. !     /* found block with MIN_HEADER_LINES */
  446.   
  447. !     TEST("\nSearch for start of header\n", 0, 0);
  448.   
  449. !     for (;;) {
  450. !     fseek(f, backup_p[backup_index], 0);
  451. !     --digest.dg_lines;
  452. !     if (--backup_count == 0) break;
  453. !     decrease_index();
  454. !     if ((line_type[backup_index] & (LN_HEADER | LN_TEXT)) == 0)
  455. !         break;
  456. !     }
  457. !     if (digest.dg_lines == 0) {
  458. !     TEST("Skipped empty article\n", 0, 0);
  459. !     return -1;
  460. !     }
  461. !  found_mail_header:
  462. !     for (;;) {
  463. !     digest.dg_lpos = backup_p[backup_index];
  464. !     if (--backup_count < 0) break;
  465. !     decrease_index();
  466. !     if ((line_type[backup_index] & (LN_BLANK | LN_DASHED)) == 0)
  467. !         break;
  468. !     --digest.dg_lines;
  469.       }
  470.   
  471. !     return (digest.dg_lines == 0) ? -1 : 1;
  472.   }
  473.   
  474.   
  475. ***************
  476. *** 307,312 ****
  477. --- 453,460 ----
  478.   register char *lp;
  479.   int all;
  480.   {
  481. +     static char *dummy;
  482. +     static char namebuf[NAME_LENGTH+1];
  483.   
  484.   #define check(name, lgt, field) \
  485.       if (isascii(lp[lgt]) && isspace(lp[lgt]) && strncmp(name, lp, lgt) == 0) {\
  486. ***************
  487. *** 320,331 ****
  488.       switch (*lp++) {
  489.   
  490.        case '\001':
  491. !     if (!is_mmdf_folder && strncmp(lp, "\001\001\001\n", 4) == 0) {
  492. !         is_mmdf_folder = 1;
  493. !         digest.dg_hpos += 5;
  494. !         return NULL;
  495. !     }
  496. !     break;
  497.   
  498.        case 'D':
  499.        case 'd':
  500. --- 468,478 ----
  501.       switch (*lp++) {
  502.   
  503.        case '\001':
  504. !     /* In an mmdf folder ^A^A^A^A is skipped at beginning of header */
  505. !     if (!is_mmdf_folder) break;
  506. !     if (strncmp(lp, "\001\001\001\n", 4)) break;
  507. !     digest.dg_hpos += 5;
  508. !     return NULL;
  509.   
  510.        case 'D':
  511.        case 'd':
  512. ***************
  513. *** 335,341 ****
  514.        case 'F':
  515.        case 'f':
  516.       check("rom:",    4, dg_from);
  517. !     break;
  518.   
  519.        case 'R':
  520.        case 'r':
  521. --- 482,493 ----
  522.        case 'F':
  523.        case 'f':
  524.       check("rom:",    4, dg_from);
  525. !     if (!is_mail_folder) break;
  526. !     if (*--lp != 'F') break;
  527. !     if (is_mail_from_line(lp, namebuf) < 0) break;
  528. !     /* Store packed sender in dg_from here and return dummy to parser */
  529. !     if (digest.dg_from == NULL) digest.dg_from = namebuf;
  530. !     return &dummy;
  531.   
  532.        case 'R':
  533.        case 'r':
  534. *** ./LAST/doc/RELEASE_NOTES    Tue Sep 18 12:44:55 1990
  535. --- doc/RELEASE_NOTES    Thu Oct  4 23:05:05 1990
  536. ***************
  537. *** 1163,1168 ****
  538. --- 1163,1230 ----
  539.       than giving a new level-2 menu - or if it gave a level-2 menu
  540.       it wasn't possible to repeat the command.
  541.   
  542. + Prog:    nn
  543. + Title:    Patch 10 broke term.c on BSD based systems
  544. + From:    too many to mention (sorry)!
  545. + Fixed:    Patch #11 [term.c]
  546. + Prog:    nnadmin
  547. + Title:    Root was sometimes denied permission to Z)ap (rmgroup) a group.
  548. + From:    david@cs.uow.edu.au (David E A Wilson)
  549. + Fixed:    Patch #11 [admin.c]
  550. + Prog:    nn
  551. + Title:    Unsubscribed groups may be included by NEW in sequence.
  552. + From:    avery@netcom.UUCP (Avery Colter)
  553. + Fixed:    Patch #11 [sequence.c]
  554. + Prog:    nn
  555. + Title:    'V' {version} command not supported in reading mode.
  556. + From:    msc@mtcchi.uucp (Michael S. Cross)
  557. + Fixed:    Patch #11 [more.c]
  558. + Prog:    nn
  559. + Title:    subject-match-limit confuses qsort() - may even dump core!
  560. + From:    eggert@twinsun.com (Paul Eggert) + fix!!!!
  561. +     kravitz%foxtail@ucsd.edu (Jody Kravitz)
  562. +     Mark Nagel <nagel@ICS.UCI.EDU>
  563. +     boutilie@motcid.UUCP (Eric Boutilier)
  564. +     Nick Holloway <alfie@cs.warwick.ac.uk>
  565. + Fixed:    Patch #11 [sort.c nn.1]
  566. +     Paul Eggert: It is possible to construct headers H,I,J for three
  567. +     different articles such that H<I, I<J, and J<H.  If subject-match-
  568. +     limit is 1, then the following values for H,I,J cause the anomaly:
  569. +             subject    t_stamp
  570. +         H    ay    0
  571. +         I    a    1
  572. +         J    ax    2
  573. +     The fix CHANGES the semantics of the subject-match-limit to be
  574. +     a length check only.
  575. + Prog:    nn
  576. + Title:    G some.group can only enter some.group once.
  577. + From:    David Lesher <wb8foz@mthvax.cs.miami.edu>
  578. +     liz@grian.cps.altadena.ca.us (Liz Allen-Mitchell)
  579. + Fixed:    Patch #11 [group.c]
  580. + Prog:    nn
  581. + Title:    Compressing an mmdf folder did not save in mmdf format.
  582. + From:    Curtis Galloway <curtisg@sco.com> + fix
  583. + Fixed:    Patch #11 [folder.c save.c]
  584. + Prog:    aux
  585. + Title:    Some "good" messages from inews (various versions) are seen as fatal.
  586. + From:    heiby@mcdchg.chg.mcd.mot.com (Ron Heiby) + fix
  587. +     Nick Sayer <mrapple@quack.sac.ca.us> + fix
  588. + Fixed:    Patch #11 [aux.sh]
  589. + Prog:    nnmaster
  590. + Title:    The nnmaster should attempt to connect again (-r) rather than stop
  591. +     in case of network errors when connecting to nntp server.
  592. + From:    olson%anchor.esd@sgi.com (Dave Olson) + fixes
  593. + Fixed:    Patch #11 [nntp.c global.c]
  594.   
  595.   New features since initial 6.4.0 release
  596.   ----------------------------------------
  597. ***************
  598. *** 1510,1512 ****
  599. --- 1572,1637 ----
  600.       generation of Distribution: headers.
  601.   From:    KFS
  602.   Added:    Patch #10 [answer.c variable.c]
  603. + Prog:    nn
  604. + Title:    ~user/ is now expanded in file names
  605. + From:    bernd@bhpcpd.kembla.oz.au (Bernd Wechner) & KFS
  606. + Added:    Patch #11 [folder.c]
  607. + Prog:    xmakefile
  608. + Title:    Added support for Symmetry style parallel make
  609. + From:    Kareth & Jaap Vermeulen
  610. + Added:    Patch #11 [xmakefile conf/m-symmetry.h]
  611. + Prog:    nn
  612. + Title:    If "check-group-access" is set, access to a group (including
  613. +     the menu) is now prohibited.
  614. + From:    KFS on request from David Paul Zimmerman <dpz@action.rutgers.edu>
  615. + Added:    Patch #11 [db.c variable.c]
  616. + Prog:    nn
  617. + Title:    A variable can now be locked, eg. "lock check-group-access" to
  618. +     prevent a user from modifying the variable.
  619. + From:    KFS
  620. + Added:    Patch #11 [init.c variable.c]
  621. + Prog:    nn
  622. + Title:    The init file "LIB/setup" is now loaded first independent of -I option.
  623. + From:    KFS on request from David Paul Zimmerman <dpz@action.rutgers.edu>
  624. + Added:    Patch #11 [init.c]
  625. + Prog:    nn
  626. + Title:    nn will now look for a ".defaultnewsrc" on first startup in the
  627. +     NEWS_LIB, CLIENT, and DB directories (var: initial-newsrc-file).
  628. + From:    KFS on request from David Paul Zimmerman <dpz@action.rutgers.edu>
  629. + Added:    Patch #11 [newsrc.c]
  630. + Prog:    nn
  631. + Title:    Stricter splitting of folders and mail boxes.
  632. + From:    bernd@bhpcpd.kembla.oz.au (Bernd Wechner) & KFS
  633. + Added:    Patch #11 [folder.c digest.c variable.c]
  634. +     Folders are now split according to the format of the first article in
  635. +     the folder, i.e. a folder is now either in "standard", "UNIX mail",
  636. +     or MMDF format, and it is no longer possible to have "mixed" folders.
  637. + Prog:    nn
  638. + Title:    Existing folders are now scanned to determine the proper format for
  639. +     saved articles (unless the variable 'folder-format-check' is unset.)
  640. + From:    KFS
  641. + Added:    Patch #11 [save.c variable.c]
  642. +     This means that the variables 'mail-format' and 'mmdf-format'
  643. +     are only used to determine the format of new folders, or if
  644. +     'folder-format-check' is off (if you know only one format is used).
  645. + Prog:    nn
  646. + Title:    Improved rewriting of folders and mail boxes.
  647. + From:    bernd@bhpcpd.kembla.oz.au (Bernd Wechner) & KFS
  648. + Added:    Patch #11 [folder.c global.c variable.c]
  649. +     The backup folder is now located in the .nn directory (there is a new
  650. +     variable backup-folder-path to control this), and a folder can now be
  651. +     rewritten even if the directory is read only.  The backup folder is
  652. +     retained if the keep-backup-folder variable is set.  A trace of the
  653. +     compression process is now shown if trace-folder-packing is set.
  654. *** ./LAST/folder.c    Mon Jul 16 17:38:41 1990
  655. --- folder.c    Sat Sep 29 01:26:59 1990
  656. ***************
  657. *** 10,23 ****
  658. --- 10,64 ----
  659.   #include "news.h"
  660.   #include "term.h"
  661.   #include "menu.h"
  662. + #include <pwd.h>
  663.   
  664. + import char *home_directory;
  665. + import int  current_folder_type;
  666. + import int  use_mail_folders;
  667. + import int  use_mmdf_folders;
  668.   export int  dont_sort_folders = 0;
  669.   export char *folder_directory  = NULL;
  670. + export int  folder_rewrite_trace = 1;
  671. + export char *backup_folder_path = "BackupFolder~";
  672. + export int  keep_backup_folder = 1;
  673. + export int  convert_folder_mode = 0; /* ignore folder's format on rewrite */
  674.   
  675.   import int fmt_linenum;
  676.   import char *header_lines;
  677.   
  678.   /*
  679. +  *    expand ~[user][/...] form
  680. +  *    src ptr is advanced to last char of user name.
  681. +  */
  682. + static char *tilde_expansion(srcp, compl)
  683. + char **srcp;
  684. + int compl;
  685. + {
  686. +     struct passwd *pwd, *getpwnam();
  687. +     register char *name = *srcp;
  688. +     register char *tail, x;
  689. +     tail = ++name; /* skip ~ */
  690. +     while (*tail && isascii(*tail) && !isspace(*tail) && *tail != '/')
  691. +     tail++;
  692. +     if (compl && *tail != '/') return NULL;
  693. +     if (tail == name) return home_directory;
  694. +     *srcp = tail - 1;
  695. +     x = *tail;
  696. +     *tail = NUL;
  697. +     pwd = getpwnam(name);
  698. +     if (pwd == NULL && !compl)
  699. +     msg("User %s not found", name);
  700. +     *tail = x;
  701. +     return (pwd == NULL) ? NULL : pwd->pw_dir;
  702. + }
  703. + /*
  704.    *     file name completion and expansion
  705.    *
  706.    *    expand_mode bits:
  707. ***************
  708. *** 25,30 ****
  709. --- 66,72 ----
  710.    *        2:    don't expand $N
  711.    *        4:    don't expand any $?  (but $(...) is expanded)
  712.    *        8:    don't complain about ~... (shell will do that)
  713. +  *        10:    doing filename completion
  714.    */
  715.   
  716.   
  717. ***************
  718. *** 56,69 ****
  719.           }
  720.   
  721.           if ((expand_mode & 1) && c == '~') {
  722. !         if (src[1] != '/') {
  723. !             if (expand_mode & 8) goto copy;
  724. !             msg("Can't handle ~user expansion (yet)");
  725.               return 0;
  726.           }
  727.   
  728. -         cp = home_directory;
  729.            cp_str:
  730.           while (*cp) *dp++ = *cp++;
  731.           if (dp[-1] != '/') *dp++ = '/';
  732. --- 98,107 ----
  733.           }
  734.   
  735.           if ((expand_mode & 1) && c == '~') {
  736. !         if ((cp = tilde_expansion(&src, (expand_mode & 0x10))) == NULL) {
  737.               return 0;
  738.           }
  739.   
  740.            cp_str:
  741.           while (*cp) *dp++ = *cp++;
  742.           if (dp[-1] != '/') *dp++ = '/';
  743. ***************
  744. *** 187,193 ****
  745.       if (*path == '|') return -1;    /* no completion for pipes */
  746.   
  747.       if (*path == '+' || *path == '~') {
  748. !         if (!expand_file_name(nbuf, path, 1))
  749.           return 0;    /* no completions */
  750.       } else
  751.           strcpy(nbuf, path);
  752. --- 225,231 ----
  753.       if (*path == '|') return -1;    /* no completion for pipes */
  754.   
  755.       if (*path == '+' || *path == '~') {
  756. !         if (!expand_file_name(nbuf, path, 0x11))
  757.           return 0;    /* no completions */
  758.       } else
  759.           strcpy(nbuf, path);
  760. ***************
  761. *** 288,299 ****
  762.       return ME_NO_REDRAW;
  763.       }
  764.   
  765.       was_raw = no_raw();
  766.       s_keyboard = 0;
  767.   
  768. -     printf("\rReading: %-.65s", path);
  769. -     clrline();
  770.       current_group = &fake_group;
  771.   
  772.       mark_memory(&mem_marker);
  773. --- 326,349 ----
  774.       return ME_NO_REDRAW;
  775.       }
  776.   
  777. +     switch (get_folder_type(folder)) {
  778. +      case 0:
  779. +     msg("Reading: %-.65s", path);    break;
  780. +      case 1:
  781. +      case 2:
  782. +     msg("Reading %s folder: %-.50s", 
  783. +         current_folder_type==1 ? "mail" : "mmdf", path);
  784. +     break;
  785. +      default:
  786. +     msg("Folder is empty");
  787. +     fclose(folder);
  788. +     return ME_NO_REDRAW;
  789. +     }    
  790. +     rewind(folder);
  791.       was_raw = no_raw();
  792.       s_keyboard = 0;
  793.   
  794.       current_group = &fake_group;
  795.   
  796.       mark_memory(&mem_marker);
  797. ***************
  798. *** 301,307 ****
  799.       ah = alloc_art();
  800.   
  801.       more = 1;
  802. !     while (more && (more = get_digest_article(folder, dgbuf)) >= 0) {
  803.       if (s_keyboard) break;
  804.   
  805.       ah->a_number = 0;
  806. --- 351,357 ----
  807.       ah = alloc_art();
  808.   
  809.       more = 1;
  810. !     while (more && (more = get_digest_article(folder, dgbuf, 1)) >= 0) {
  811.       if (s_keyboard) break;
  812.   
  813.       ah->a_number = 0;
  814. ***************
  815. *** 346,353 ****
  816.   
  817.       fclose(folder);
  818.   
  819. -     if (was_raw) raw();
  820.       if (s_keyboard) {
  821.       menu_cmd = ME_NO_REDRAW;
  822.       } else
  823. --- 396,401 ----
  824. ***************
  825. *** 381,387 ****
  826.       }
  827.       if (mode == 0 && cancel_count) {
  828.           clrdisp();
  829. !         printf("Folder: %s\nFile:   %s\n\n", folder_name, folder_file);
  830.           if (cancel_count == n_articles)
  831.           printf("Cancel all articles and remove folder? ");
  832.           else
  833. --- 429,435 ----
  834.       }
  835.       if (mode == 0 && cancel_count) {
  836.           clrdisp();
  837. !         printf("\rFolder: %s\n\rFile:   %s\n\n\r", folder_name, folder_file);
  838.           if (cancel_count == n_articles)
  839.           printf("Cancel all articles and remove folder? ");
  840.           else
  841. ***************
  842. *** 393,401 ****
  843.            case 1:
  844.           printf("\n\n");
  845.           if (cancel_count == n_articles) {
  846. !             if (unlink(group_path_name) < 0) {
  847. !             printf("Could not unlink %s\n", group_path_name);
  848. !             sleep(3);
  849.               }
  850.           } else
  851.               rewrite_folder();
  852. --- 441,450 ----
  853.            case 1:
  854.           printf("\n\n");
  855.           if (cancel_count == n_articles) {
  856. !             if (unlink(group_path_name) < 0 &&
  857. !             truncate(group_path_name, (off_t)0) < 0) {
  858. !             printf("\rCould not unlink %s\n\r", group_path_name);
  859. !             any_key(0);
  860.               }
  861.           } else
  862.               rewrite_folder();
  863. ***************
  864. *** 415,420 ****
  865. --- 464,471 ----
  866.       header_lines = orig_hdr_lines;
  867.       }
  868.   
  869. +     if (was_raw) raw();
  870.       return menu_cmd;
  871.   }
  872.   
  873. ***************
  874. *** 422,496 ****
  875.   rewrite_folder()
  876.   {
  877.       register FILE *src, *dst;
  878. !     char oldfile[FILENAME], *sp;
  879.       register int c;
  880.       register long cnt;
  881.       register article_header *ah, **ahp;
  882.       register article_number n;
  883.   
  884. !     if ((src = fopen(group_path_name, "r")) == NULL) {
  885. !     msg("Cannot open %s", group_path_name);
  886. !     return;
  887.       }
  888.   
  889. !     strcpy(oldfile, group_path_name);
  890. !     sp = strrchr(oldfile, '/');
  891. !     strcpy((sp == NULL ? oldfile : sp+1), "~OLD~FOLDER~");
  892. !     unlink(oldfile);
  893. !     if (link(group_path_name, oldfile) < 0) goto move_error;
  894. !     if (unlink(group_path_name) < 0) {
  895. !     if (unlink(oldfile) == 0) goto move_error;
  896. !     printf("\n\n%s was linked to %s --- cannot proceed\n",
  897. !            group_path_name, oldfile);
  898. !     sleep(5);
  899. !     return;
  900.       }
  901.   
  902. !     if ((dst = fopen(group_path_name, "w")) == NULL) {
  903.       fclose(src);
  904.       goto move_back;
  905.       }
  906.   
  907.       sort_articles(0);
  908.   
  909. !     printf("Compressing folder..."); fl;
  910.   
  911.       for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
  912.       ah = *ahp;
  913.       if (ah->attr == A_CANCEL) continue;
  914.       fseek(src, ah->hpos, 0);
  915. !     cnt = ah->lpos - ah->hpos;
  916.       while (--cnt >= 0) {
  917.           if ((c = getc(src)) == EOF) break;
  918.           putc(c, dst);
  919.       }
  920. !     putc(NL, dst);
  921.       }
  922.       fclose(src);
  923. !     if (ferror(dst)) {
  924. !     fclose(dst);
  925. !     goto move_back;
  926. !     }
  927. !     unlink(oldfile);
  928.       return;
  929.   
  930. ! move_back:
  931. !     if (link(oldfile, group_path_name) == 0) {
  932. !     unlink(oldfile);
  933. !     printf("Cannot create new file -- Folder restored\n");
  934. !     sleep(2);
  935. !     } else {
  936. !     printf("Cannot create new file\n\nFolder saved in %s\n",
  937. !            oldfile);
  938. !     sleep(10);
  939. !     }
  940. !     return;
  941.   
  942. ! move_error:
  943. !     fclose(src);
  944. !     printf("\n\nCannot move folder %s to %s\n",
  945. !        group_path_name, oldfile);
  946. !     sleep(3);
  947. !     return;
  948.   }
  949. --- 473,540 ----
  950.   rewrite_folder()
  951.   {
  952.       register FILE *src, *dst;
  953. !     char *oldfile, *sp;
  954.       register int c;
  955.       register long cnt;
  956.       register article_header *ah, **ahp;
  957.       register article_number n;
  958.   
  959. !     if (strchr(backup_folder_path, '/'))
  960. !     oldfile = backup_folder_path;
  961. !     else
  962. !     oldfile = relative(nn_directory, backup_folder_path);
  963. !     if (move_file(group_path_name, oldfile, 1) < 0) {
  964. !     printf("\r\n\nCannot backup folder in %s\n", oldfile);
  965. !     goto confirm;
  966.       }
  967.   
  968. !     if ((src = open_file(oldfile, OPEN_READ)) == NULL) {
  969. !     printf("\rCannot open %s\n\r", oldfile);
  970. !     goto move_back;
  971.       }
  972.   
  973. !     if ((dst = open_file(group_path_name, OPEN_CREATE)) == NULL) {
  974.       fclose(src);
  975. +     printf("\rCannot create %s\n\r", group_path_name);
  976.       goto move_back;
  977.       }
  978.   
  979.       sort_articles(0);
  980.   
  981. !     printf("\rCompressing folder...\n\r"); fl;
  982. !     get_folder_type(src);
  983.   
  984.       for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
  985.       ah = *ahp;
  986. +     cnt = ah->lpos - ah->hpos;
  987. +     if (folder_rewrite_trace)
  988. +         printf("%s\t%s (%ld-%ld=%ld)\n\r",
  989. +            ah->attr == A_CANCEL ? "CANCEL" : "KEEP",
  990. +            ah->subject, (long)(ah->hpos), (long)(ah->lpos), cnt);
  991.       if (ah->attr == A_CANCEL) continue;
  992.       fseek(src, ah->hpos, 0);
  993. !     mailbox_format(dst, -1);
  994.       while (--cnt >= 0) {
  995.           if ((c = getc(src)) == EOF) break;
  996.           putc(c, dst);
  997.       }
  998. !     mailbox_format(dst, 0);
  999.       }
  1000.       fclose(src);
  1001. !     if (fclose(dst) == EOF) goto move_back;
  1002. !     if (!keep_backup_folder) unlink(oldfile);
  1003. !     if (folder_rewrite_trace) goto confirm;
  1004.       return;
  1005.   
  1006. !  move_back:
  1007. !     if (move_file(oldfile, group_path_name, 2) == 0)
  1008. !     printf("Cannot create new file -- Folder restored\n\r");
  1009. !     else
  1010. !     printf("Cannot create new file\n\n\rFolder saved in %s\n\r", oldfile);
  1011.   
  1012. !  confirm:
  1013. !     any_key(0);
  1014.   }
  1015. *** ./LAST/global.c    Tue Sep 18 12:44:57 1990
  1016. --- global.c    Fri Oct  5 18:23:21 1990
  1017. ***************
  1018. *** 39,44 ****
  1019. --- 39,48 ----
  1020.   
  1021.   export char version_id[32];
  1022.   
  1023. + #ifdef NNTP
  1024. + export int use_nntp = 0;    /* bool: t iff we use nntp */
  1025. + #endif
  1026.   export unsigned short user_eid;
  1027.   export unsigned short user_id, group_id;
  1028.   export int process_id;
  1029. ***************
  1030. *** 357,362 ****
  1031. --- 361,387 ----
  1032.       return NULL;
  1033.   }
  1034.   
  1035. + FILE *open_file_search_path(name, mode)
  1036. + char *name;
  1037. + int mode;
  1038. + {
  1039. +     FILE *f;
  1040. +     if (name == NULL) return NULL;
  1041. +     if (*name == '/') return open_file(name, mode);
  1042. +     
  1043. +     f = NULL;
  1044. +     if (!use_nntp)
  1045. +     f = open_file(relative(news_lib_directory, name), OPEN_READ);
  1046. +     if (f == NULL)
  1047. +     f= open_file(relative(lib_directory, name), OPEN_READ);
  1048. +     if (f == NULL)
  1049. +     f = open_file(relative(db_directory, name), OPEN_READ);
  1050. +     return f;
  1051. + }
  1052. +     
  1053.   fgets_multi(buf, size, f)
  1054.   char *buf;
  1055.   int size;
  1056. ***************
  1057. *** 452,462 ****
  1058.   char *name;
  1059.   char *mode;
  1060.   {
  1061. !     struct stat statb;
  1062.       extern int errno;
  1063.       int mask;
  1064.   
  1065. !     if (stat(name, &statb)) return 0;
  1066.   
  1067.       if (mode == NULL) return statb.st_mtime;
  1068.   
  1069. --- 477,487 ----
  1070.   char *name;
  1071.   char *mode;
  1072.   {
  1073. !     static struct stat statb;
  1074.       extern int errno;
  1075.       int mask;
  1076.   
  1077. !     if (name != NULL && stat(name, &statb)) return 0;
  1078.   
  1079.       if (mode == NULL) return statb.st_mtime;
  1080.   
  1081. ***************
  1082. *** 499,504 ****
  1083. --- 524,637 ----
  1084.       return statb.st_mtime;
  1085.   }
  1086.   
  1087. + /*
  1088. +  * copy_file: copy (or append) src file to dest file.
  1089. +  *
  1090. +  * Returns number of characters copied or an error code:
  1091. +  *  -1: source file not found
  1092. +  *  -2: cannot create destination
  1093. +  *  -3: write error
  1094. +  */
  1095. + int32 copy_file(src, dest, append)
  1096. + char *src, *dest;
  1097. + int append;
  1098. + {
  1099. +     register FILE *s, *d;
  1100. +     register int32 n = 0;
  1101. +     register int c;
  1102. +     s = open_file(src, OPEN_READ);
  1103. +     if (s == NULL) return -1;
  1104. +     d = open_file(dest, append ? OPEN_APPEND : OPEN_CREATE);
  1105. +     if (d == NULL) {
  1106. +     fclose(s);
  1107. +     return -2;
  1108. +     }
  1109. +     n = 0;
  1110. +     while ((c = getc(s)) != EOF) {
  1111. +     putc(c, d);
  1112. +     n++;
  1113. +     }
  1114. +     fclose(s);
  1115. +     if (fclose(d) == EOF) {
  1116. +     if (!append) unlink(dest);
  1117. +     return -3;
  1118. +     }
  1119. +     return n;
  1120. + }
  1121. + /*
  1122. +  * move_file: move old file to new file, linking if possible.
  1123. +  *
  1124. +  * The third arg determines what is acceptable if the old file cannot be
  1125. +  * removed after copying to the new file:
  1126. +  *   0: must remove old, else remove new and fail,
  1127. +  *   1: must remove or truncate old, else remove new and fail,
  1128. +  *   2: just leave old if it cannot be removed or truncated.
  1129. +  *    
  1130. +  * Returns positive value for success, negative for failure:
  1131. +  *   0: file renamed (link)
  1132. +  *   1: file copied, old removed
  1133. +  *   2: file copied, but old file is only truncated.
  1134. +  *   3: file copied, but old file still exist.
  1135. +  *  -1: source file not found
  1136. +  *  -2: cannot create destination
  1137. +  *  -3: write error
  1138. +  *  -4: cannot unlink/truncate old
  1139. +  *  -5: cannot unlink new
  1140. +  *  -6: cannot link old to new
  1141. +  *  -9: messy situation: old and new linked on return (cannot happen?)
  1142. +  */
  1143. + move_file(old, new, may_keep_old)
  1144. + char *old, *new;
  1145. + int may_keep_old;
  1146. + {
  1147. +     int32 n;
  1148. +     if (file_exist(new, (char *)NULL)) {
  1149. +     if (file_exist((char *)NULL, "d"))
  1150. +         return -5;
  1151. +     if (unlink(new) < 0)    /* careful - new may be directory ? */
  1152. +         switch (errno) {
  1153. +          case ENOENT:
  1154. +         break;
  1155. +          case EACCES:
  1156. +         if (file_exist((char *)NULL, "w")) goto do_copy;
  1157. +          default:
  1158. +         return -5;
  1159. +         }
  1160. +     }
  1161. +     
  1162. +     if (link(old, new) < 0)
  1163. +     switch (errno) {
  1164. +      case EACCES:    /* can just as well try to copy */
  1165. +      case EXDEV:
  1166. +         goto do_copy;
  1167. +      default:
  1168. +         return -6;
  1169. +     }
  1170. +     
  1171. +     if (unlink(old) == 0)
  1172. +     return 0;
  1173. +     /* we were able to link but not unlink old    */
  1174. +     /* remove new, and attempt a copy instead    */
  1175. +     if (unlink(new) < 0) return -9; /* cannot happen? */
  1176. +  do_copy:
  1177. +     if ((n = copy_file(old, new, 0)) < 0) return n;
  1178. +     if (unlink(old) == 0) return 1;
  1179. +     if (may_keep_old)
  1180. +     if (n == 0 || truncate(old, (off_t)0) == 0) return 2;
  1181. +     if (may_keep_old == 2) return 3;
  1182. +     unlink(new);
  1183. +     return -4;
  1184. + }
  1185.   
  1186.   #ifdef HAVE_SYSLOG
  1187.   #include <syslog.h>
  1188. ***************
  1189. *** 531,538 ****
  1190.       return 1;
  1191.   }
  1192.   
  1193. ! static mail_sys_error(err)
  1194.   char *err;
  1195.   {
  1196.       FILE *f;
  1197.       char cmd[FILENAME*2];
  1198. --- 664,672 ----
  1199.       return 1;
  1200.   }
  1201.   
  1202. ! static mail_sys_error(err, isfatal)
  1203.   char *err;
  1204. + int isfatal;
  1205.   {
  1206.       FILE *f;
  1207.       char cmd[FILENAME*2];
  1208. ***************
  1209. *** 541,553 ****
  1210.       strcpy(cmd, FATAL_ERROR_MAIL_CMD);
  1211.   #else
  1212.   #ifdef MAILX
  1213. !     sprintf(cmd, "%s -s 'nnmaster fatal error' %s", MAILX, OWNER);
  1214.   #else
  1215.       sprintf(cmd, "mail %s", OWNER);
  1216.   #endif
  1217.   #endif
  1218.       if ((f = popen(cmd, "w")) == NULL) return;
  1219. !     fprintf(f, "nnmaster terminated\n\nFatal system error:\n%s\n", err);
  1220.       pclose(f);
  1221.   }
  1222.   
  1223. --- 675,690 ----
  1224.       strcpy(cmd, FATAL_ERROR_MAIL_CMD);
  1225.   #else
  1226.   #ifdef MAILX
  1227. !     sprintf(cmd, "%s -s 'nnmaster %s' %s", MAILX,
  1228. !         isfatal ? "fatal error" : "warning", OWNER);
  1229.   #else
  1230.       sprintf(cmd, "mail %s", OWNER);
  1231.   #endif
  1232.   #endif
  1233.       if ((f = popen(cmd, "w")) == NULL) return;
  1234. !     fprintf(f, "nnmaster %s\n\n%system error:\n%s\n",
  1235. !         isfatal ? "terminated" : "warning",
  1236. !         isfatal ? "Fatal s" : "S", err);
  1237.       pclose(f);
  1238.   }
  1239.   
  1240. ***************
  1241. *** 570,576 ****
  1242.       end_vararg;
  1243.   
  1244.       if (who_am_i == I_AM_MASTER) {
  1245. !     mail_sys_error(buf);
  1246.       if (dont_write_console) nn_exit(7);
  1247.   #ifndef HAVE_SYSLOG
  1248.       f = open_file("/dev/console", OPEN_CREATE);
  1249. --- 707,713 ----
  1250.       end_vararg;
  1251.   
  1252.       if (who_am_i == I_AM_MASTER) {
  1253. !     mail_sys_error(buf, 1);
  1254.       if (dont_write_console) nn_exit(7);
  1255.   #ifndef HAVE_SYSLOG
  1256.       f = open_file("/dev/console", OPEN_CREATE);
  1257. ***************
  1258. *** 587,592 ****
  1259. --- 724,770 ----
  1260.       user_error("%s", buf);
  1261.   }
  1262.   
  1263. + /*
  1264. +  *    sys_warning: like sys_error but MASTER will return with -1
  1265. +  *    instead of exit.  Clients still terminate!
  1266. +  */
  1267. + /*VARARGS*/
  1268. + sys_warning(va_alist)
  1269. + va_dcl
  1270. + {
  1271. +     char buf[512];
  1272. +     char *fmt;
  1273. +     FILE *f;
  1274. +     use_vararg;
  1275. +     start_vararg;
  1276. +     enter_log('R', va_args1toN);
  1277. +     end_vararg;
  1278. +     start_vararg;
  1279. +     fmt = va_arg1(char *);
  1280. +     vsprintf(buf, fmt, va_args2toN);
  1281. +     end_vararg;
  1282. +     if (who_am_i != I_AM_MASTER)
  1283. +     user_error("%s", buf);
  1284. +     mail_sys_error(buf, 0);
  1285. +     if (dont_write_console) return -1;
  1286. + #ifndef HAVE_SYSLOG
  1287. +     if((f = open_file("/dev/console", OPEN_CREATE)) != NULL) {
  1288. +     fprintf(f, "\n\rNNMASTER WARNING\n\r%s\n\n\r", buf);
  1289. +     fclose(f);
  1290. +     }
  1291. + #else /* HAVE_SYSLOG */
  1292. +     openlog("nnmaster", LOG_CONS, LOG_DAEMON);
  1293. +     syslog(LOG_ALERT, "%s", buf);
  1294. +     closelog();
  1295. + #endif /* HAVE_SYSLOG */
  1296. +     return -1;
  1297. + }
  1298.   /*VARARGS*/
  1299.   log_entry(va_alist)
  1300.   va_dcl
  1301. ***************
  1302. *** 770,773 ****
  1303. --- 948,976 ----
  1304.           path, mode, path);
  1305.       return system(command) != 0 ? -1 : 0;
  1306.   }
  1307. + #endif
  1308. + #ifndef HAVE_TRUNCATE
  1309. + truncate(path, len)
  1310. + char *path;
  1311. + off_t len;
  1312. + {
  1313. +     int fd;
  1314. +     struct stat st;
  1315. +     if (len != 0)
  1316. +     sys_error("truncate(%s,%ld): non-zero length", path, (long)len);
  1317. + #ifdef O_TRUNC
  1318. +     fd = open(path, O_WRONLY | O_TRUNC);
  1319. + #else
  1320. +     if (stat(path, &st) < 0) return -1;
  1321. +     fd = creat(path, st.st_mode & 07777);
  1322. + #endif    
  1323. +     if (fd < 0) return -1;
  1324. +     close(fd);
  1325. +     return 0;
  1326. + }
  1327.   #endif
  1328. *** ./LAST/group.c    Tue Sep 18 12:44:59 1990
  1329. --- group.c    Mon Sep 24 13:13:59 1990
  1330. ***************
  1331. *** 686,691 ****
  1332. --- 686,692 ----
  1333.           msg("Group %s already active", gh->group_name);
  1334.           goto_return(ME_NO_REDRAW);
  1335.       }
  1336. +     if (o_cur_first < 0) o_cur_first = 0;
  1337.       gh->current_first = gh->last_db_article + 1;
  1338.       }
  1339.   
  1340. *** ./LAST/init.c    Thu Jul 19 18:12:14 1990
  1341. --- init.c    Thu Oct  4 19:04:47 1990
  1342. ***************
  1343. *** 178,183 ****
  1344. --- 178,187 ----
  1345.       extern FILE *loc_seq_hook, *glob_seq_hook;
  1346.       char *next_arg;
  1347.   
  1348. +     in_init = 1;
  1349. +     load_init_file(relative(lib_directory, "setup"), (FILE **)NULL, 0);
  1350. +     in_init = 0;
  1351.       if (first_arg && strncmp(first_arg, "-I", 2) == 0) {
  1352.       if (first_arg[2] == NUL) return;
  1353.       first_arg += 2;
  1354. ***************
  1355. *** 240,245 ****
  1356. --- 244,250 ----
  1357.       "help",            4,    2,
  1358.       "load",            4,    0,
  1359.       "local",            5,    3,
  1360. +     "lock",            4,    3,
  1361.       "make",            4,    -1,
  1362.       "make map ",        9,    -2,
  1363.       "man",            3,    0,
  1364. ***************
  1365. *** 907,912 ****
  1366. --- 912,923 ----
  1367.       CASE( "toggle" ) {
  1368.           if (argv(1) == NULL) goto stx_err;
  1369.           toggle_variable(argv(1));
  1370. +         break;
  1371. +     }
  1372. +     CASE( "lock" ) {
  1373. +         if (argv(1) == NULL) goto stx_err;
  1374. +         lock_variable(argv(1));
  1375.           break;
  1376.       }
  1377.   
  1378. *** ./LAST/man/nn.1.C    Tue Sep 18 12:45:06 1990
  1379. --- man/nn.1.C    Fri Oct  5 18:43:05 1990
  1380. ***************
  1381. *** 1101,1113 ****
  1382.   .B n
  1383.   command).
  1384.   .TP
  1385. ! \fBsubject-match-limit\fP \fIlength\fP    (integer, default 20)
  1386. ! If one article's subject is identical to the first part of another
  1387. ! article, the two subjects will still be considered identical if the
  1388. ! length of the shorter subject is at least the limit set by this
  1389. ! variable.  This is mainly used to get articles whose subject line has
  1390. ! been truncated for some reason (who said notefiles?) aligned with the
  1391. ! proper set of articles anyway.
  1392.   .TP
  1393.   \fBsubject-match-offset\fP \fIoffset\fP    (integer, default 0)
  1394.   When set to a positive number, that many characters at the beginning
  1395. --- 1101,1110 ----
  1396.   .B n
  1397.   command).
  1398.   .TP
  1399. ! \fBsubject-match-limit\fP \fIlength\fP    (integer, default 256)
  1400. ! Subjects will be considered identical if their first \fIlength\fP
  1401. ! characters match.  Setting this uncritically to a low value may
  1402. ! cause unexpected results!
  1403.   .TP
  1404.   \fBsubject-match-offset\fP \fIoffset\fP    (integer, default 0)
  1405.   When set to a positive number, that many characters at the beginning
  1406. *** ./LAST/more.c    Tue Sep 18 12:45:08 1990
  1407. --- more.c    Thu Sep 20 22:05:48 1990
  1408. ***************
  1409. *** 1038,1043 ****
  1410. --- 1038,1047 ----
  1411.       if (shell_escape()) goto redraw;
  1412.       goto dflt_prompt;
  1413.   
  1414. +      case K_VERSION:
  1415. +     prompt(P_VERSION);
  1416. +     goto same_prompt;
  1417.        case K_EXTENDED_CMD:
  1418.       news_save = news;
  1419.       digest_save = digest;
  1420. *** ./LAST/news.c    Thu Jul 19 18:12:20 1990
  1421. --- news.c    Thu Sep 27 20:27:34 1990
  1422. ***************
  1423. *** 55,61 ****
  1424.           if (fptr = (*hdr_field)(bp, all)) {
  1425.           while (*bp && *bp != ':' && isascii(*bp) && !isspace(*bp))
  1426.               bp++;
  1427. !         bp++;
  1428.           while (*bp && isascii(*bp) && isspace(*bp) && *bp != NL) bp++;
  1429.           *fptr = bp;
  1430.           }
  1431. --- 55,61 ----
  1432.           if (fptr = (*hdr_field)(bp, all)) {
  1433.           while (*bp && *bp != ':' && isascii(*bp) && !isspace(*bp))
  1434.               bp++;
  1435. !         if (*bp) bp++;
  1436.           while (*bp && isascii(*bp) && isspace(*bp) && *bp != NL) bp++;
  1437.           *fptr = bp;
  1438.           }
  1439. *** ./LAST/newsrc.c    Tue Sep 18 12:45:09 1990
  1440. --- newsrc.c    Thu Oct  4 22:30:46 1990
  1441. ***************
  1442. *** 26,31 ****
  1443. --- 26,32 ----
  1444.   
  1445.   export int  keep_rc_backup = 1;
  1446.   export char *bak_suffix = ".bak";
  1447. + export char *initial_newsrc_path = ".defaultnewsrc";
  1448.   
  1449.   export int  no_update = 0;
  1450.   export int  use_selections = 1;
  1451. ***************
  1452. *** 337,343 ****
  1453.       }
  1454.   
  1455.       rc = open_file(newsrc_file, OPEN_READ);
  1456. !     if (rc == NULL) goto new_user;
  1457.   
  1458.       while (fgets(rcbuf, RC_LINE_MAX, rc) != NULL) {
  1459.       gh = NULL;
  1460. --- 338,351 ----
  1461.       }
  1462.   
  1463.       rc = open_file(newsrc_file, OPEN_READ);
  1464. !     if (rc == NULL) {
  1465. !     extern FILE *open_file_search_path();
  1466. !     rc = open_file_search_path(initial_newsrc_path, OPEN_READ);
  1467. !     if (rc == NULL) goto new_user;
  1468. !     /* ignore groups not in the initial newsrc file */
  1469. !     last_new_gh = ACTIVE_GROUP(master.number_of_groups - 1);
  1470. !     last_new_group = last_new_gh->creation_time;
  1471. !     }
  1472.   
  1473.       while (fgets(rcbuf, RC_LINE_MAX, rc) != NULL) {
  1474.       gh = NULL;
  1475. *** ./LAST/nntp.c    Thu Jul 19 18:12:21 1990
  1476. --- nntp.c    Fri Oct  5 18:23:22 1990
  1477. ***************
  1478. *** 47,53 ****
  1479.   import char *db_directory, *tmp_directory, *news_active;
  1480.   
  1481.   export char nntp_server[256];    /* name of nntp server */
  1482. - export int use_nntp = 0;    /* bool: t iff we use nntp */
  1483.   export int nntp_failed = 0;    /* bool: t iff connection is broken in
  1484.                      nntp_get_article() or nntp_get_active() */
  1485.   
  1486. --- 47,52 ----
  1487. ***************
  1488. *** 63,68 ****
  1489. --- 62,68 ----
  1490.   import char *sys_errlist[];
  1491.   extern int user_error();
  1492.   extern int sys_error();
  1493. + extern int sys_warning();
  1494.   
  1495.   #define syserr() (errno >= 0 && errno < sys_nerr ? \
  1496.             sys_errlist[errno] : "Unknown error.")
  1497. ***************
  1498. *** 259,265 ****
  1499.   /*
  1500.    * get_socket:  get a connection to the nntp server.
  1501.    *
  1502. !  *     Doesn't return in case of errors.
  1503.    */
  1504.   
  1505.   static get_socket()
  1506. --- 259,268 ----
  1507.   /*
  1508.    * get_socket:  get a connection to the nntp server.
  1509.    *
  1510. !  * Errors can happen when YP services or DNS are temporarily down or
  1511. !  * hung, so we log errors and return failure rather than exitting if we
  1512. !  * are the master.  The effects of retrying every 15 minutes (or whatever
  1513. !  * the -r interval is) are not that bad.  Dave Olson, SGI
  1514.    */
  1515.   
  1516.   static get_socket()
  1517. ***************
  1518. *** 276,287 ****
  1519.   #endif
  1520.   
  1521.       if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  1522. !     sys_error("nntp/tcp: Unknown service.\n");
  1523.   
  1524.       s = who_am_i == I_AM_MASTER ? 10 : 2;
  1525.       while ((hp = gethostbyname(nntp_server)) == NULL) {
  1526. !     if (--s < 0)
  1527. !         sys_error("NNTP server %s unknown.\n", nntp_server);
  1528.       sleep(10);
  1529.       }
  1530.   
  1531. --- 279,289 ----
  1532.   #endif
  1533.   
  1534.       if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  1535. !     return sys_warning("nntp/tcp: Unknown service.\n");
  1536.   
  1537.       s = who_am_i == I_AM_MASTER ? 10 : 2;
  1538.       while ((hp = gethostbyname(nntp_server)) == NULL) {
  1539. !     if (--s < 0) goto host_err;
  1540.       sleep(10);
  1541.       }
  1542.   
  1543. ***************
  1544. *** 300,309 ****
  1545.   #ifdef h_addr
  1546.       /* get a socket and initiate connection -- use multiple addresses */
  1547.   
  1548.       for (cp = hp->h_addr_list; cp && *cp; cp++) {
  1549.       s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  1550. !     if (s < 0)
  1551. !         sys_error("Can't get NNTP socket: %s\n", syserr());
  1552.       bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  1553.   
  1554.       x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  1555. --- 302,311 ----
  1556.   #ifdef h_addr
  1557.       /* get a socket and initiate connection -- use multiple addresses */
  1558.   
  1559. +     s = x = -1;
  1560.       for (cp = hp->h_addr_list; cp && *cp; cp++) {
  1561.       s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  1562. !     if (s < 0) goto sock_err;
  1563.       bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  1564.   
  1565.       x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  1566. ***************
  1567. *** 314,352 ****
  1568.       (void) close(s);
  1569.       s = -1;
  1570.       }
  1571. !     if (x < 0 && who_am_i != I_AM_MASTER)
  1572. !     user_error("Giving up on NNTP server %s!\n", nntp_server);
  1573.   #else                    /* no name server */
  1574.   #ifdef EXCELAN
  1575.       if ((s = socket(SOCK_STREAM, NULL, &sin, SO_KEEPALIVE)) < 0)
  1576. !     sys_error("Can't get NNTP socket: %s", syserr());
  1577.       sin.sin_port = htons(IPPORT_NNTP);
  1578.       machine = nntp_server;
  1579. !     if ((sin.sin_addr.s_addr = rhost(&machine)) == -1)
  1580. !         sys_error("%s:  Unknown host.", nntp_server);
  1581.   
  1582.       /* And then connect */
  1583. !     if (connect(s, &sin) < 0) {
  1584. !     if (who_am_i == I_AM_MASTER)
  1585. !         sys_error("Connecting to %s: %s", nntp_server, syserr());
  1586. !         (void) close(s);
  1587. !     s = -1;
  1588. !     }
  1589.   #else /* not EXCELAN */
  1590.       if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  1591. !     sys_error("Can't get NNTP socket: %s\n", syserr());
  1592.       /* And then connect */
  1593.       bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  1594. !     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  1595. !     if (who_am_i == I_AM_MASTER)
  1596. !         sys_error("Connecting to %s failed: %s", nntp_server, syserr());
  1597. !     s = -1;
  1598. !     }
  1599.   #endif  /* EXCELAN */
  1600.   #endif
  1601.       return s;
  1602.   }
  1603.   
  1604.   /*
  1605. --- 316,363 ----
  1606.       (void) close(s);
  1607.       s = -1;
  1608.       }
  1609. !     if (x < 0)
  1610. !     sys_warning("Giving up on NNTP server %s!", nntp_server);
  1611.   #else                    /* no name server */
  1612.   #ifdef EXCELAN
  1613.       if ((s = socket(SOCK_STREAM, NULL, &sin, SO_KEEPALIVE)) < 0)
  1614. !     goto sock_err;
  1615. !     
  1616.       sin.sin_port = htons(IPPORT_NNTP);
  1617.       machine = nntp_server;
  1618. !     if ((sin.sin_addr.s_addr = rhost(&machine)) == -1) {
  1619. !     (void) close(s);
  1620. !     goto host_err;
  1621. !     }
  1622.   
  1623.       /* And then connect */
  1624. !     if (connect(s, &sin) < 0)
  1625. !     goto conn_err;
  1626.   #else /* not EXCELAN */
  1627.       if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  1628. !     goto sock_err;
  1629. !     
  1630.       /* And then connect */
  1631.       bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  1632. !     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0)
  1633. !     goto conn_err;
  1634.   #endif  /* EXCELAN */
  1635.   #endif
  1636.       return s;
  1637. +  host_err:
  1638. +     sys_warning("NNTP server %s unknown.\n", nntp_server);
  1639. +     return -1;
  1640. +  sock_err:
  1641. +     sys_warning("Can't get NNTP socket: %s", syserr());
  1642. +     return -1;
  1643. +  conn_err:
  1644. +     (void) close(s);
  1645. +     if (who_am_i == I_AM_MASTER)
  1646. +     sys_warning("Connecting to %s failed: %s", nntp_server, syserr());
  1647. +     return -1;
  1648.   }
  1649.   
  1650.   /*
  1651. *** ./LAST/patchlevel.h    Tue Sep 18 12:45:13 1990
  1652. --- patchlevel.h    Thu Oct  4 23:12:18 1990
  1653. ***************
  1654. *** 21,27 ****
  1655.    *    1990-07-16: Patch #8 (6.4.8) - HIGH
  1656.    *    1990-07-19: Patch #9 (6.4.9) - MEDIUM
  1657.    *    1990-09-18: Patch #10 (6.4.10) - HIGH
  1658.    */
  1659.   
  1660. ! #define PATCHLEVEL 10
  1661.   
  1662. --- 21,28 ----
  1663.    *    1990-07-16: Patch #8 (6.4.8) - HIGH
  1664.    *    1990-07-19: Patch #9 (6.4.9) - MEDIUM
  1665.    *    1990-09-18: Patch #10 (6.4.10) - HIGH
  1666. +  *    1990-10-05: Patch #11 (6.4.11) - HIGH
  1667.    */
  1668.   
  1669. ! #define PATCHLEVEL 11
  1670.   
  1671. *** ./LAST/save.c    Tue Sep 18 12:45:14 1990
  1672. --- save.c    Mon Oct  1 17:15:46 1990
  1673. ***************
  1674. *** 22,27 ****
  1675. --- 22,28 ----
  1676.   export int  quick_save = 0;
  1677.   export int  conf_append = 0;
  1678.   export int  conf_create = 1;
  1679. + export int  folder_format_check = 1;
  1680.   
  1681.   export char *saved_header_escape = "~";
  1682.   
  1683. ***************
  1684. *** 47,52 ****
  1685. --- 48,55 ----
  1686.   import int  shell_restrictions;
  1687.   import char delayed_msg[];
  1688.   
  1689. + import int current_folder_type;
  1690.   import int rot13_active;
  1691.   
  1692.   static int save_mode;
  1693. ***************
  1694. *** 151,156 ****
  1695. --- 154,173 ----
  1696.       return 1;
  1697.   }
  1698.   
  1699. + set_folder_type(name)
  1700. + char *name;
  1701. + {        
  1702. +     FILE *f;
  1703. +     current_folder_type = -1;
  1704. +     if (!folder_format_check) return;
  1705. +     if ((f = open_file(name, OPEN_READ)) != NULL) {
  1706. +     get_folder_type(f);
  1707. +     fclose(f);
  1708. +     }
  1709. + }
  1710.   char *init_save(command, mode_textp)
  1711.   char command;
  1712.   char **mode_textp;
  1713. ***************
  1714. *** 487,494 ****
  1715.           fclose(art);
  1716.           return 0;
  1717.           }
  1718. !         if (ftell(save_file) != (off_t)0)
  1719.           save_mode &= ~FILE_IS_NEW;
  1720.       }
  1721.       }
  1722.   
  1723. --- 504,514 ----
  1724.           fclose(art);
  1725.           return 0;
  1726.           }
  1727. !         current_folder_type = -1;
  1728. !         if (ftell(save_file) != (off_t)0) {
  1729. !         if (mode != NO_HEADER) set_folder_type(save_name);
  1730.           save_mode &= ~FILE_IS_NEW;
  1731. +         }
  1732.       }
  1733.       }
  1734.   
  1735. ***************
  1736. *** 627,632 ****
  1737. --- 647,656 ----
  1738.       return;
  1739.       }
  1740.       fseek(h, (off_t)0, 2);
  1741. +     if (ftell(h) > 0)
  1742. +     set_folder_type(file);
  1743. +     else
  1744. +     current_folder_type = -1;
  1745.       if (!use_mmdf_folders && ftell(h) > 0) putc(NL, h);  /* just in case */
  1746.       mailbox_format(h, 1);
  1747.       endpos = ftell(f) - ah->hpos;
  1748. ***************
  1749. *** 644,651 ****
  1750.   {
  1751.       time_t now;
  1752.       char *ctime();
  1753.   
  1754. !     if (use_mmdf_folders) {
  1755.       fprintf(f, "\001\001\001\001\n");
  1756.       return 0;
  1757.       }
  1758. --- 668,692 ----
  1759.   {
  1760.       time_t now;
  1761.       char *ctime();
  1762. +     int do_mmdf, do_mail;
  1763. +     do_mmdf = do_mail = 0;
  1764. +     switch (current_folder_type) {
  1765. +      case 0:
  1766. +     break;
  1767. +      case 1:
  1768. +     do_mail = 1;
  1769. +     break;
  1770. +      case 2:
  1771. +     do_mmdf = 1;
  1772. +     break;
  1773. +      default:
  1774. +     do_mmdf = use_mmdf_folders;
  1775. +     do_mail = use_mail_folders;
  1776. +     break;
  1777. +     }
  1778.   
  1779. !     if (do_mmdf) {
  1780.       fprintf(f, "\001\001\001\001\n");
  1781.       return 0;
  1782.       }
  1783. ***************
  1784. *** 655,661 ****
  1785.       return 1;
  1786.       }
  1787.   
  1788. !     if (use_mail_folders) {
  1789.       now = cur_time();
  1790.       fprintf(f, "From %s %s",
  1791.           (use_path_in_from && news.ng_path) ? news.ng_path :
  1792. --- 696,702 ----
  1793.       return 1;
  1794.       }
  1795.   
  1796. !     if (top > 0 && do_mail) {
  1797.       now = cur_time();
  1798.       fprintf(f, "From %s %s",
  1799.           (use_path_in_from && news.ng_path) ? news.ng_path :
  1800. *** ./LAST/sequence.c    Tue Sep 18 12:45:14 1990
  1801. --- sequence.c    Wed Sep 19 18:40:55 1990
  1802. ***************
  1803. *** 669,674 ****
  1804. --- 669,675 ----
  1805.   
  1806.        case GS_NEW_GROUP:
  1807.           if ((gh->group_flag & G_NEW) == 0) continue;
  1808. +         if (gh->group_flag & G_UNSUBSCRIBED) continue;
  1809.           break;
  1810.   
  1811.        case GS_PREFIX0:
  1812. *** ./LAST/sort.c    Tue Sep 18 12:45:15 1990
  1813. --- sort.c    Thu Oct  4 22:27:30 1990
  1814. ***************
  1815. *** 9,15 ****
  1816. --- 9,24 ----
  1817.   
  1818.   
  1819.   
  1820. + #ifdef BAD_ORDER_SUBJ_DATE
  1821. + /* If one article's subject is identical to the first part of another
  1822. +  article, the two subjects will still be considered identical if the
  1823. +  length of the shorter subject is at least the limit set by this variable. */
  1824.   export int subject_match_limit = 20;     /* "strncmp" limit for subjects */
  1825. + #else
  1826. + /* Subjects are considered identical if their first s-m-l characters match */
  1827. + export int subject_match_limit = 256;     /* "strncmp" limit for subjects */
  1828. + #endif
  1829.   export int match_skip_prefix = 0; /* skip first N bytes in matches */
  1830.   export int match_parts_equal = 0; /* match digits as equal */
  1831.   
  1832. ***************
  1833. *** 78,95 ****
  1834. --- 87,112 ----
  1835.       }
  1836.       }
  1837.   
  1838. + #ifdef BAD_ORDER_SUBJ_DATE
  1839.       for (len = 0; ; len++, a++, b++) {
  1840. + #else
  1841. +     for (len = subject_match_limit; --len >= 0; a++, b++) {
  1842. + #endif
  1843.       while ((ca = *a) && MATCH_DROP(match_subject, ca)) a++;
  1844.       while ((cb = *b) && MATCH_DROP(match_subject, cb)) b++;
  1845.   
  1846.       if (ca == NUL) {
  1847.           if (cb == NUL) break;
  1848. + #ifdef BAD_ORDER_SUBJ_DATE
  1849.           if (len >= subject_match_limit) break;
  1850. + #endif
  1851.           return -1;
  1852.       }
  1853.   
  1854.       if (cb == NUL) {
  1855. + #ifdef BAD_ORDER_SUBJ_DATE
  1856.           if (len >= subject_match_limit) break;
  1857. + #endif
  1858.           return 1;
  1859.       }
  1860.   
  1861. *** ./LAST/term.c    Tue Sep 18 12:45:16 1990
  1862. --- term.c    Tue Sep 18 15:33:39 1990
  1863. ***************
  1864. *** 825,832 ****
  1865. --- 825,834 ----
  1866.       do_flush_input = 1;
  1867.   #else
  1868.   #ifdef FREAD
  1869. +   {
  1870.       int arg = FREAD;
  1871.       ioctl(0, TIOCFLUSH, &arg);
  1872. +   }
  1873.   #else
  1874.       ioctl(0, TIOCFLUSH, 0);
  1875.   #endif
  1876. *** ./LAST/variable.c    Tue Sep 18 12:45:17 1990
  1877. --- variable.c    Thu Oct  4 23:12:16 1990
  1878. ***************
  1879. *** 11,16 ****
  1880. --- 11,17 ----
  1881.   import in_init;
  1882.   
  1883.   import char            /* string variables */
  1884. +     *backup_folder_path,
  1885.       *bak_suffix,
  1886.       *bug_address,
  1887.       *decode_header_file,
  1888. ***************
  1889. *** 26,31 ****
  1890. --- 27,33 ----
  1891.       *folder_directory,
  1892.       included_mark[],
  1893.       *inews_program,
  1894. +     *initial_newsrc_path,
  1895.       *mail_box,
  1896.       *mail_record,
  1897.       *mail_script,
  1898. ***************
  1899. *** 60,65 ****
  1900. --- 62,68 ----
  1901.       auto_select_subject,
  1902.       auto_preview_mode,
  1903.       case_fold_search,
  1904. +     check_group_access,
  1905.       compress_mode,
  1906.       conf_append,
  1907.       conf_auto_quit,
  1908. ***************
  1909. *** 81,91 ****
  1910. --- 84,97 ----
  1911.       flow_control,
  1912.       flush_typeahead,
  1913.       fmt_rptsubj,
  1914. +     folder_format_check,
  1915. +     folder_rewrite_trace,
  1916.       ignore_xon_xoff,
  1917.       include_art_id,
  1918.       include_full_header,
  1919.       include_mark_blanks,
  1920.       inews_pipe_input,
  1921. +     keep_backup_folder,
  1922.       keep_rc_backup,
  1923.       keep_unsubscribed,
  1924.       keep_unsub_long,
  1925. ***************
  1926. *** 163,168 ****
  1927. --- 169,175 ----
  1928.       scroll_last_lines,
  1929.       show_purpose_mode,
  1930.       slow_speed,
  1931. +     strict_from_parse,
  1932.       sort_mode,
  1933.       subject_match_limit,
  1934.       wrap_headers;
  1935. ***************
  1936. *** 200,205 ****
  1937. --- 207,213 ----
  1938.   #define V_SAFE        0x0100
  1939.   #define V_INIT        0x0200
  1940.   
  1941. + #define V_LOCKED    0x0800
  1942.   #define V_MODIFIED    0x8000
  1943.   
  1944.   #define    STR        V_STRING |
  1945. ***************
  1946. *** 232,240 ****
  1947. --- 240,250 ----
  1948.       "auto-read-mode-limit",    INT 0,        (char **)&auto_read_limit,
  1949.       "auto-select-subject",    BOOL 0,        (char **)&auto_select_subject,
  1950.       "backup",            BOOL 0,        (char **)&keep_rc_backup,
  1951. +     "backup-folder-path",    STR 4,        (char **)&backup_folder_path,
  1952.       "backup-suffix",        STR 0,        (char **)&bak_suffix,
  1953.       "bug-report-address",     STR 0,        (char **)&bug_address,
  1954.       "case-fold-search",        BOOL 0,        (char **)&case_fold_search,
  1955. +     "check-group-access",    BOOL 0,        (char **)&check_group_access,
  1956.       "collapse-subject",        INT 3,        (char **)&collapse_subject,
  1957.       "columns",            INT 1,        (char **)&Columns,
  1958.       "comp1-key",        KEY 0,        (char **)&comp1_key,
  1959. ***************
  1960. *** 272,279 ****
  1961. --- 282,291 ----
  1962.       "flow-control",        BOOL 0,        (char **)&flow_control,
  1963.       "flush-typeahead",        BOOL 0,        (char **)&flush_typeahead,
  1964.       "folder",            STR 2,        (char **)&folder_directory,
  1965. +     "folder-format-check",    BOOL 0,        (char **)&folder_format_check,
  1966.       "folder-save-file",        STR 3,        (char **)&folder_save_file,
  1967.       "follow-distribution",    STR 0,        (char **)&distribution_follow,
  1968. +     "from-line-parsing",    INT 0,        (char **)&strict_from_parse,
  1969.       "fsort",            BOOL 2,        (char **)&dont_sort_folders,
  1970.       "header-lines",        STR 0,        (char **)&header_lines,
  1971.       "help-key",            KEY 0,        (char **)&help_key,
  1972. ***************
  1973. *** 284,289 ****
  1974. --- 296,303 ----
  1975.       "included-mark",        STR 1,        (char **)included_mark,
  1976.       "inews",            STR 0,        (char **)&inews_program,
  1977.       "inews-pipe-input",        BOOL 0,        (char **)&inews_pipe_input,
  1978. +     "initial-newsrc-file",    STR 0,        (char **)&initial_newsrc_path,
  1979. +     "keep-backup-folder",     BOOL 0,        (char **)&keep_backup_folder,
  1980.       "keep-unsubscribed",     BOOL 0,        (char **)&keep_unsubscribed,
  1981.       "kill",            BOOL 0,        (char **)&do_kill_handling,
  1982.       "kill-debug",        BOOL 0,        (char **)&kill_debug,
  1983. ***************
  1984. *** 375,380 ****
  1985. --- 389,395 ----
  1986.       "suggest-default-save",     BOOL 0,        (char **)&suggest_save_file,
  1987.       "tidy-newsrc",        BOOL 0,        (char **)&tidy_newsrc,
  1988.       "time",            BOOL 0,        (char **)&show_current_time,
  1989. +     "trace-folder-packing",    BOOL 0,        (char **)&folder_rewrite_trace,
  1990.       "trusted-escape-codes",    STR 0,        (char **)&trusted_escapes,
  1991.       "unshar-command",        STR SAFE 1,    (char **)unshar_command,
  1992.       "unshar-header-file",    STR 0,        (char **)&unshar_header_file,
  1993. ***************
  1994. *** 496,501 ****
  1995. --- 511,521 ----
  1996.       }
  1997.       }
  1998.   
  1999. +     if (var->var_flags & V_LOCKED) {
  2000. +     msg("Variable '%s' is locked", variable);
  2001. +     return 0;
  2002. +     }
  2003.       if (!on || val_string == NULL)
  2004.       value = 0;
  2005.       else
  2006. ***************
  2007. *** 531,540 ****
  2008. --- 551,568 ----
  2009.           break;
  2010.   
  2011.        case 3:
  2012. +      case 4:
  2013.           if (!on || val_string == NULL) {
  2014.           msg("Cannot unset string `%s'", variable);
  2015.           break;
  2016.           }
  2017. +         if (VAR_OP == 4) {
  2018. +         char exp_buf[FILENAME];
  2019. +         if (expand_file_name(exp_buf, val_string, 1)) {
  2020. +             STR_VAR = copy_str(exp_buf);
  2021. +             break;
  2022. +         }
  2023. +         }
  2024.           STR_VAR = copy_str(val_string);
  2025.           break;
  2026.       }
  2027. ***************
  2028. *** 670,675 ****
  2029. --- 698,712 ----
  2030.       BOOL_VAR = !BOOL_VAR;
  2031.   }
  2032.   
  2033. + lock_variable(variable)
  2034. + char *variable;
  2035. + {
  2036. +     register struct variable_defs *var;
  2037. +     if ((var = lookup_variable(variable)) != NULL)
  2038. +     var->var_flags |= V_LOCKED;
  2039. + }
  2040.   
  2041.   static char *var_value(var, tag)
  2042.   register struct variable_defs *var;
  2043. ***************
  2044. *** 681,686 ****
  2045. --- 718,724 ----
  2046.   
  2047.       if (tag != NULL)
  2048.       *tag = var_on_stack(var) ? '>' :
  2049. +     (var->var_flags & V_LOCKED) ? '!' :
  2050.       (var->var_flags & V_MODIFIED) ? '*' : ' ';
  2051.   
  2052.       switch (VAR_TYPE) {
  2053. ***************
  2054. *** 898,903 ****
  2055. --- 936,942 ----
  2056.        case 0:    /* if we update one of these variables,    */
  2057.        case 2:    /* new storage will be allocated for it */
  2058.        case 3:    /* so it is ok just to save the pointer */
  2059. +      case 4:
  2060.           vs->value.str = STR_VAR;
  2061.           break;
  2062.   
  2063. ***************
  2064. *** 956,961 ****
  2065. --- 995,1001 ----
  2066.            case 0:    /* only restore the string if changed; then we    */
  2067.            case 2:    /* can also free the memory occupied by the    */
  2068.            case 3:    /* 'new' value (if not NULL)            */
  2069. +          case 4:
  2070.           if (STR_VAR != vs->value.str) {
  2071.               if (STR_VAR != NULL) freeobj(STR_VAR);
  2072.               STR_VAR = vs->value.str;
  2073. *** ./LAST/xmakefile    Tue Jun 12 11:47:04 1990
  2074. --- xmakefile    Wed Aug 22 20:16:40 1990
  2075. ***************
  2076. *** 24,29 ****
  2077. --- 24,38 ----
  2078.   #define NNTP_EXTRA_LIB
  2079.   #endif
  2080.   
  2081. + * Symmetry style parallel make
  2082. + #undef PARALLEL
  2083. + #ifdef PARALLEL_MAKE
  2084. + #define PARALLEL &
  2085. + #else
  2086. + #define PARALLEL
  2087. + #endif
  2088.   #ifdef HAVE_ROUTING
  2089.   #define NNMAIL
  2090.   #else
  2091. ***************
  2092. *** 53,59 ****
  2093.   *
  2094.   
  2095.   BIN_PROG =    nn NNMAIL nnusage nngrab nnstats ACCOUNT
  2096. ! BIN_LINK =    nncheck nnadmin nntidy nngoback nngrep nnpost
  2097.   LIB_PROG =    aux upgrade_rc
  2098.   MASTER_PROG =    nnmaster back_act nnspew
  2099.   
  2100. --- 62,68 ----
  2101.   *
  2102.   
  2103.   BIN_PROG =    nn NNMAIL nnusage nngrab nnstats ACCOUNT
  2104. ! BIN_LINK =    nncheck nnadmin nntidy nngoback nngrep nnpost nnbatch
  2105.   LIB_PROG =    aux upgrade_rc
  2106.   MASTER_PROG =    nnmaster back_act nnspew
  2107.   
  2108. ***************
  2109. *** 85,99 ****
  2110.   
  2111.   master: $(MASTER_PROG) inst
  2112.   
  2113. ! nn:    $(NN)
  2114.       @echo linking nn
  2115.       @$(CC) $(CFLAGS) $(NN) -o nn TERMLIB EXTRA_LIB NNTP_EXTRA_LIB
  2116.   
  2117. ! nnmaster: $(MASTER)
  2118.       @echo linking nnmaster
  2119.       @$(CC) $(CFLAGS) $(MASTER) -o nnmaster EXTRA_LIB NNTP_EXTRA_LIB
  2120.   
  2121. ! nnmail:    $(MAIL)
  2122.       @echo linking nnmail
  2123.       @$(CC) $(CFLAGS) $(MAIL) EXTRA_LIB -o nnmail
  2124.   
  2125. --- 94,108 ----
  2126.   
  2127.   master: $(MASTER_PROG) inst
  2128.   
  2129. ! nn:    PARALLEL $(NN)
  2130.       @echo linking nn
  2131.       @$(CC) $(CFLAGS) $(NN) -o nn TERMLIB EXTRA_LIB NNTP_EXTRA_LIB
  2132.   
  2133. ! nnmaster: PARALLEL $(MASTER)
  2134.       @echo linking nnmaster
  2135.       @$(CC) $(CFLAGS) $(MASTER) -o nnmaster EXTRA_LIB NNTP_EXTRA_LIB
  2136.   
  2137. ! nnmail:    PARALLEL $(MAIL)
  2138.       @echo linking nnmail
  2139.       @$(CC) $(CFLAGS) $(MAIL) EXTRA_LIB -o nnmail
  2140.   
  2141. ***************
  2142. *** 112,118 ****
  2143.   upgrade_rc: upgrade_rc.sh prefix
  2144.       cat prefix upgrade_rc.sh > upgrade_rc ; chmod +x upgrade_rc
  2145.   
  2146. ! nnacct: $(ACCT)
  2147.       @echo linking nnacct
  2148.       @$(CC) $(CFLAGS) $(ACCT) EXTRA_LIB -o nnacct
  2149.   
  2150. --- 121,127 ----
  2151.   upgrade_rc: upgrade_rc.sh prefix
  2152.       cat prefix upgrade_rc.sh > upgrade_rc ; chmod +x upgrade_rc
  2153.   
  2154. ! nnacct: PARALLEL $(ACCT)
  2155.       @echo linking nnacct
  2156.       @$(CC) $(CFLAGS) $(ACCT) EXTRA_LIB -o nnacct
  2157.   
  2158. ***************
  2159. *** 125,131 ****
  2160.   prefix:    config.h mkprefix
  2161.       ./mkprefix prefix < /dev/null > prefix
  2162.   
  2163. ! mkprefix: prefix.o global.o
  2164.       $(CC) $(CFLAGS) prefix.o global.o EXTRA_LIB -o mkprefix
  2165.   
  2166.   *
  2167. --- 134,140 ----
  2168.   prefix:    config.h mkprefix
  2169.       ./mkprefix prefix < /dev/null > prefix
  2170.   
  2171. ! mkprefix: PARALLEL prefix.o global.o
  2172.       $(CC) $(CFLAGS) prefix.o global.o EXTRA_LIB -o mkprefix
  2173.   
  2174.   *
  2175.